package com.hanwei.uap.common.util;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.ByteOrder;
import java.util.List;

/**
 * 字节及字节数组操作工具类
 * 原生工具类：
 * @see java.nio.ByteBuffer,java.nio.Bits
 * @author zhaoam
 * @create 2017-08-02 9:50
 **/
public class ByteUtil {
    /**
     * hex字符串转换为byte[]
     *
     * @param hex
     * @return
     */
    public static byte[] hex2byte(String hex) {
        return hex2bytes(hex, false);
    }

    public static byte[] hex2bytes(String hex) {
        return hex2bytes(hex, false);
    }

    public static byte[] hex2bytes(String hex, boolean reverse) {
        if (hex == null || hex.isEmpty()) return null;
        byte[] bytes = new byte[hex.length() / 2];
        if (reverse) {
            for (int i = 0, j = 0; j < bytes.length; j++) {
                bytes[bytes.length - 1 - j] = (byte) Integer.parseInt(hex.substring(i, i + 2), 16);
                i += 2;
            }
        } else {
            for (int i = 0, j = 0; j < bytes.length; j++) {
                bytes[j] = (byte) Integer.parseInt(hex.substring(i, i + 2), 16);
                i += 2;
            }
        }
        return bytes;
    }

    public static String byte2hex(byte b) {
        String hex = Integer.toHexString(b & 0xFF);
        if (hex.length() == 1)
            hex = "0" + hex;
        return hex.toUpperCase();
    }
    /**
     * byte[]转换为hex字符串，从数组头部开始
     *
     * @param bytes
     * @return
     */
    public static String byte2hex(byte[] bytes) {
        return byte2hex(bytes, false, false);
    }

    public static String byte2hex(byte[] bytes, boolean withTag) {
        return byte2hex(bytes, false, withTag);
    }

    /**
     * byte[]转换为hex字符串
     *
     * @param bytes
     * @param reverse true:从数组尾部开始,false从数组头部开始
     * @return
     */
    public static String byte2hex(byte[] bytes, boolean reverse, boolean withTag) {
        if (bytes == null || bytes.length == 0) return null;
        StringBuffer buf = new StringBuffer(bytes.length * 2);
        if (reverse)
            for (int i = bytes.length - 1; i >= 0; i--) {
                if (withTag) {
                    if (i < bytes.length - 1)
                        buf.append(StringX.BLANK_STRING);
                    buf.append("0x");
                }
                String hex = Integer.toHexString(bytes[i] & 0xFF);
                if (hex.length() == 1)
                    buf.append('0');
                buf.append(hex.toUpperCase());
            }
        else
            for (int i = 0; i < bytes.length; i++) {
                if (withTag) {
                    if (i > 0)
                        buf.append(StringX.BLANK_STRING);
                    buf.append("0x");
                }
                String hex = Integer.toHexString(bytes[i] & 0xFF);
                if (hex.length() == 1)
                    buf.append('0');
                buf.append(hex.toUpperCase());
            }
        return buf.toString();
    }

    /**
     * 将两个ASCII字符合成一个字节；
     * 如："EF"--> 0xEF
     * @param src0 byte
     * @param src1 byte
     * @return byte
     */
    public static byte unionBytes(byte src0, byte src1) {
        byte _b0 = Byte.decode("0x" + new String(new byte[] { src0 })).byteValue();
        _b0 = (byte) (_b0 << 4);
        byte _b1 = Byte.decode("0x" + new String(new byte[] { src1 })).byteValue();
        byte ret = (byte) (_b0 ^ _b1);
        return ret;
    }

    public static byte[] arraycopy(byte[] src, int start, int len) {
        byte[] buf = new byte[len];
        System.arraycopy(src, start, buf, 0, len);
        return buf;
    }

    public static byte[] merge(byte[] buf1, byte[] buf2) {
        return merge(buf1, buf2, null, null);
    }

    public static byte[] merge(byte[] buf1, byte[] buf2, byte[] buf3, byte[] buf4) {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            if (buf1 != null) baos.write(buf1);
            if (buf2 != null) baos.write(buf2);
            if (buf3 != null) baos.write(buf3);
            if (buf4 != null) baos.write(buf4);
            return baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static byte[] merge(List<byte[]> slices) {
        if (slices == null) return null;
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            for (int i = 0; i < slices.size(); i++) {
                if (null != slices.get(i))
                    baos.write(slices.get(i));
            }
            return baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static int byte2unsignint(byte highByte, byte lowByte) {
        return (highByte & 0xff) << 8 | (lowByte & 0xff);
    }

    public static int byte2int(byte highByte, byte lowByte) {
        return highByte << 8 | (lowByte & 0xff);
    }

    /**
     * 字节数组转换为int，高位在前低位在后
     * @param bytes
     * @return
     */
    public static int bytes2int(byte[] bytes) {
        return bytes2int(bytes,false);
    }

    /**
     * 字节数组转换为int
     * @param bytes
     * @param reverse true:little-endian小端模式 低位字节在前，高位字节在后;false:big-endian大端模式 高位字节在前,低位字节在后
     * @return
     */
    public static int bytes2int(byte[] bytes, boolean reverse) {
        return bytes2int(bytes, reverse, true);
    }

    public static int bytes2unsignint(byte[] bytes, boolean reverse) {
        return bytes2int(bytes, reverse, false);
    }


    /**
     * 字节数组转换为int
     * @param bytes
     * @param reverse true:little-endian小端模式 低位字节在前，高位字节在后;false:big-endian大端模式 高位字节在前,低位字节在后
     * @param signed true:有符号整数;false:无符号整数
     * @return
     */
    public static int bytes2int(byte[] bytes, boolean reverse, boolean signed) {
        int value = 0;
        int temp;
        if (reverse)
            for (int i = 0; i < bytes.length; i++) {
                if(signed && i == bytes.length-1)
                    temp = bytes[i] << (i * 8);
                else
                    temp = (bytes[i] & 0xff) << (i * 8);
                value |= temp;
            }
        else
            for (int i = 0; i < bytes.length; i++) {
                value <<= 8;
                if(signed && i == 0)
                    temp = bytes[i];
                else
                    temp = bytes[i] & 0xff;
                value |= temp;
            }
        return value;
    }

    /**
     * float转换为byte[4]数组
     * @param f 浮点数
     * @return
     */
    public static byte[] getByteArray(float f) {
        int intbits = Float.floatToIntBits(f);//将float里面的二进制串解释为int整数
        return int2bytes(intbits);
    }

    /**
     * 从byte数组的index处的连续4个字节获得一个float
     * @param bytes
     * @param index
     * @return
     */
    public static float getFloat(byte[] bytes, int index) {
        return Float.intBitsToFloat(bytes2int(bytes, index));
    }

    /**
     * 双精度浮点数
     * @param d
     * @return
     */
    public static byte[] getByteArray(double d) {
        long longbits = Double.doubleToLongBits(d);
        return long2bytes(longbits);
    }

    /**
     * 从byte数组的index处的连续8个字节获得一个double
     * @param bytes 字节数组
     * @param index 开始位置
     * @return
     */
    public static double getDouble(byte[] bytes, int index) {
        return Double.longBitsToDouble(getLong(bytes, index, ByteOrder.BIG_ENDIAN));
    }

    /**
     * int转换为byte[4]数组
     * @param i 短整型
     * @param byteOrder 字节模式,枚举类型：BIG_ENDIAN,LITTLE_ENDIAN
     * @return
     */
    public static byte[] getByteArray(int i, ByteOrder byteOrder) {
        byte[] b = new byte[4];
        if (ByteOrder.BIG_ENDIAN == byteOrder) {
            b[0] = (byte) ((i & 0xff000000) >> 24);
            b[1] = (byte) ((i & 0x00ff0000) >> 16);
            b[2] = (byte) ((i & 0x0000ff00) >> 8);
            b[3] = (byte) (i & 0x000000ff);
        } else {
            b[3] = (byte) ((i & 0xff000000) >> 24);
            b[2] = (byte) ((i & 0x00ff0000) >> 16);
            b[1] = (byte) ((i & 0x0000ff00) >> 8);
            b[0] = (byte) (i & 0x000000ff);
        }
        return b;
    }

    /**
     * 从byte数组的index处的连续4个字节获得一个int
     *
     * @param bytes     字节数组
     * @param index     开始位置
     * @param byteOrder 字节模式,枚举类型：BIG_ENDIAN,LITTLE_ENDIAN
     * @return
     */
    public static int getInt(byte[] bytes, int index, ByteOrder byteOrder) {
        if (ByteOrder.BIG_ENDIAN == byteOrder) {
            return (0xff000000 & (bytes[index + 0] << 24)) |
                    (0x00ff0000 & (bytes[index + 1] << 16)) |
                    (0x0000ff00 & (bytes[index + 2] << 8)) |
                    (0x000000ff & bytes[index + 3]);
        } else {
            return (0xff000000 & (bytes[index + 3] << 24)) |
                    (0x00ff0000 & (bytes[index + 2] << 16)) |
                    (0x0000ff00 & (bytes[index + 1] << 8)) |
                    (0x000000ff & bytes[index + 0]);
        }
    }

    /**
     *
     * @param l 长整型
     * @param byteOrder 字节模式,枚举类型：BIG_ENDIAN,LITTLE_ENDIAN
     * @return
     */
    public static byte[] getByteArray(long l, ByteOrder byteOrder) {
        byte b[] = new byte[8];
        if (ByteOrder.BIG_ENDIAN == byteOrder) {
            b[0] = (byte) (0xff & (l >> 56));
            b[1] = (byte) (0xff & (l >> 48));
            b[2] = (byte) (0xff & (l >> 40));
            b[3] = (byte) (0xff & (l >> 32));
            b[4] = (byte) (0xff & (l >> 24));
            b[5] = (byte) (0xff & (l >> 16));
            b[6] = (byte) (0xff & (l >> 8));
            b[7] = (byte) (0xff & l);
        } else {
            b[7] = (byte) (0xff & (l >> 56));
            b[6] = (byte) (0xff & (l >> 48));
            b[5] = (byte) (0xff & (l >> 40));
            b[4] = (byte) (0xff & (l >> 32));
            b[3] = (byte) (0xff & (l >> 24));
            b[2] = (byte) (0xff & (l >> 16));
            b[1] = (byte) (0xff & (l >> 8));
            b[0] = (byte) (0xff & l);
        }
        return b;
    }

    /**
     * 从byte数组的index处的连续8个字节获得一个long
     *
     * @param bytes     字节数组
     * @param index     开始位置
     * @param byteOrder 字节模式,枚举类型：BIG_ENDIAN,LITTLE_ENDIAN
     * @return
     */
    public static long getLong(byte[] bytes, int index, ByteOrder byteOrder) {
        if (ByteOrder.BIG_ENDIAN == byteOrder) {
            return (0xff00000000000000L & ((long) bytes[index + 0] << 56)) |
                    (0x00ff000000000000L & ((long) bytes[index + 1] << 48)) |
                    (0x0000ff0000000000L & ((long) bytes[index + 2] << 40)) |
                    (0x000000ff00000000L & ((long) bytes[index + 3] << 32)) |
                    (0x00000000ff000000L & ((long) bytes[index + 4] << 24)) |
                    (0x0000000000ff0000L & ((long) bytes[index + 5] << 16)) |
                    (0x000000000000ff00L & ((long) bytes[index + 6] << 8)) |
                    (0x00000000000000ffL & (long) bytes[index + 7]);
        } else {
            return (0xff00000000000000L & ((long) bytes[index + 7] << 56)) |
                    (0x00ff000000000000L & ((long) bytes[index + 6] << 48)) |
                    (0x0000ff0000000000L & ((long) bytes[index + 5] << 40)) |
                    (0x000000ff00000000L & ((long) bytes[index + 4] << 32)) |
                    (0x00000000ff000000L & ((long) bytes[index + 3] << 24)) |
                    (0x0000000000ff0000L & ((long) bytes[index + 2] << 16)) |
                    (0x000000000000ff00L & ((long) bytes[index + 1] << 8)) |
                    (0x00000000000000ffL & (long) bytes[index + 0]);
        }
    }

    public static long bytes2long(byte[] bytes) {
        return bytes2long(bytes, false);
    }

    public static long bytes2unsignlong(byte[] bytes, boolean reverse) {
        return bytes2long(bytes, reverse, false);
    }

    public static long bytes2long(byte[] bytes, boolean reverse) {
        return bytes2long(bytes, reverse, true);
    }
    /**
     * 字节数组转换为long
     * @param bytes
     * @param reverse true:little-endian小端模式 低位字节在前，高位字节在后;false:big-endian大端模式 高位字节在前,低位字节在后
     * @param signed true:有符号长整数;false:无符号长整数
     * @return
     */
    public static long bytes2long(byte[] bytes, boolean reverse, boolean signed) throws NumberFormatException{
        if (bytes.length > 8) {
            throw new NumberFormatException("bytes length great than 8!");
        }
        long value = 0;
        int temp = 0;
        if (reverse) {
            for (int i = 0; i < bytes.length; i++) {
                if (signed && i == bytes.length - 1)
                    temp = bytes[i] << (i * 8);
                else
                    temp = (bytes[i] & 0xff) << (i * 8);
                value |= (long) temp;
            }
        } else {
            for (int i = 1; i <= bytes.length; i++) {
                if (signed && i == bytes.length)
                    temp = bytes[bytes.length - i] << ((i - 1) * 8);
                else
                    temp = (bytes[bytes.length - i] & 0xff) << ((i - 1) * 8);
                value |= (long) temp;
            }
        }
        return value;
    }

    public static byte[] long2bytes(long num) {
        return long2bytes(num, 8);
    }

    public static byte[] long2bytes(long num, boolean reverse) {
        return long2bytes(num, 8, reverse);
    }

    public static byte[] long2bytes(long num, int len) {
        return long2bytes(num, len, false);
    }

    public static byte[] long2bytes(long num, int len, boolean reverse) throws NumberFormatException{
        double max = Math.pow(2, len * 8) - 1;
        if (num > max) {
            throw new NumberFormatException("the number:" + num + " is great than " + max + "=2^" + len);
        }
        if (len > 8) {
            throw new NumberFormatException("the length:" + len + " is great than 8");
        }
        byte[] bytes = new byte[8];
        if (reverse) {
            bytes[0] = (byte) (num >> 0);
            bytes[1] = (byte) (num >> 8);
            bytes[2] = (byte) (num >> 16);
            bytes[3] = (byte) (num >> 24);
            bytes[4] = (byte) (num >> 32);
            bytes[5] = (byte) (num >> 40);
            bytes[6] = (byte) (num >> 48);
            bytes[7] = (byte) (num >> 56);
            if(len <8) {
                bytes = arraycopy(bytes, 0, len);
            }
        } else {
            bytes[0] = (byte) (num >> 56);
            bytes[1] = (byte) (num >> 48);
            bytes[2] = (byte) (num >> 40);
            bytes[3] = (byte) (num >> 32);
            bytes[4] = (byte) (num >> 24);
            bytes[5] = (byte) (num >> 16);
            bytes[6] = (byte) (num >> 8);
            bytes[7] = (byte) (num >> 0);
            if (len < 8) {
                bytes = arraycopy(bytes, 8 - len, len);
            }
        }
        return bytes;
    }

    public static String int2hex(int num, int len, boolean reverse) {
        byte[] bytes = int2bytes(num, len, reverse);
        return byte2hex(bytes);
    }

    public static byte[] int2bytes(int num) {
        return int2bytes(num, 4);
    }

    public static byte[] int2bytes(int num, boolean reverse) {
        return int2bytes(num, 4, reverse);
    }

    public static byte[] int2bytes(int num, int len) {
        return int2bytes(num, len, false);
    }

    /**
     *
     * @param num
     * @param len
     * @param reverse true:little-endian小端模式 低位字节在前，高位字节在后;false:big-endian大端模式 高位字节在前,低位字节在后
     * @return
     */
    public static byte[] int2bytes(int num, int len, boolean reverse) throws NumberFormatException{
        double max = Math.pow(2, len * 8) - 1;
        if (num > max) {
            throw new NumberFormatException("the number:" + num + " is great than " + max + "=2^" + len);
        }
        if (len > 4) {
            throw new NumberFormatException("the length:" + len + " is great than 4");
        }
        byte[] bytes = new byte[4];
        if (reverse) {
            bytes[0] = (byte) (num >>> 0);
            bytes[1] = (byte) (num >>> 8);
            bytes[2] = (byte) (num >>> 16);
            bytes[3] = (byte) (num >>> 24);
            if (len < 4)
                bytes = arraycopy(bytes, 0, len);

        } else {
            bytes[0] = (byte) (num >>> 24);
            bytes[1] = (byte) (num >>> 16);
            bytes[2] = (byte) (num >>> 8);
            bytes[3] = (byte) (num >>> 0);
            if (len < 4)
                bytes = arraycopy(bytes, 4 - len, len);
        }
        return bytes;
    }

    public static double bytes2double(byte[] bytes, int decimal, boolean reverse) {
        long value = bytes2long(bytes, reverse);
        double pow = Math.pow(10d, decimal);
        BigDecimal b = new BigDecimal(value);
        BigDecimal d = new BigDecimal(pow);
        BigDecimal result = b.divide(d, decimal, BigDecimal.ROUND_HALF_UP);
        return result.doubleValue();
    }
    public static double bytes2unsigndouble(byte[] bytes, int decimal, boolean reverse) {
        long value = bytes2unsignlong(bytes, reverse);
        double pow = Math.pow(10d, decimal);
        BigDecimal b = new BigDecimal(value);
        BigDecimal d = new BigDecimal(pow);
        BigDecimal result = b.divide(d, decimal, BigDecimal.ROUND_HALF_UP);
        return result.doubleValue();
    }

    public static byte[] double2bytes(double num, int decimal, int len, boolean reverse) {
        BigDecimal b = new BigDecimal(num);
        BigDecimal d = new BigDecimal(Math.pow(10d, decimal));
        long value = b.multiply(d).longValue();
        return long2bytes(value, len, reverse);
    }

    /**
     * @param bytes 字节数组
     * @param index 开始位置
     * @return
     */
    public static int bytes2int(byte[] bytes, int index) {
        return (0xff000000 & (bytes[index + 0] << 24)) |
                (0x00ff0000 & (bytes[index + 1] << 16)) |
                (0x0000ff00 & (bytes[index + 2] << 8)) |
                (0x000000ff & bytes[index + 3]);
    }

    /**
     *
     * @param num
     * @param reverse true:小端模式，false:大端模式
     * @return
     */
    public static byte[] short2bytes(short num, boolean reverse) {
        byte[] b = new byte[2];
        if (reverse) {
            b[0] = (byte) (num & 0xff);
            b[1] = (byte) ((num >> 8) & 0xff);
        } else {
            b[1] = (byte) (num & 0xff);
            b[0] = (byte) ((num >> 8) & 0xff);
        }
        return b;
    }
    public static byte[] short2bytes(short num) {
        return short2bytes(num, false);
    }

    /**
     *
     * @param bytes
     * @param reverse
     * @param signed
     * @return
     * @throws NumberFormatException
     */
    public static int bytes2short(byte[] bytes, boolean reverse, boolean signed) throws NumberFormatException {
        if (bytes.length != 2)
            throw new NumberFormatException("bytes' length ne 2!");
        int s = 0;
        if (reverse) {// 小端模式
            if (signed) {
                int s0 = bytes[0] & 0xff;// 低位
                int s1 = bytes[1] << 8;// 高位
                s = s0 | s1;
            } else {
                int s0 = bytes[0] & 0xff;// 低位
                int s1 = (bytes[1] & 0xff) << 8;// 高位
                s = s0 | s1;
            }
        } else {// 大端模式
            if (signed) {
                int s0 = bytes[0] << 8;// 高位
                int s1 = bytes[1] & 0xff;// 低位
                s = s0 | s1;
            } else {
                int s0 = (bytes[0] & 0xff) << 8;// 高位
                int s1 = bytes[1] & 0xff;// 低位
                s = s0 | s1;
            }
        }
        return s;
    }
    public static int bytes2short(byte[] bytes) throws NumberFormatException {
        return bytes2short(bytes, false, true);
    }

    /**
     * 和 bytes2short 的功能一样
     * @param bytes
     * @return
     */
    public static short bytes2Short(byte[] bytes) {
        int length = bytes.length;
        short initValue = 0;
        for (int i = 0; i < length; i++) {
            initValue <<= 8;
            initValue |= (bytes[i] & 0xff);
        }
        return initValue;
    }

    /**
     *
     * @param c
     * @param reverse true:小端模式，false:大端模式
     * @return
     */
    public static byte[] char2bytes(char c, boolean reverse) {
        byte[] b = new byte[2];
        if (reverse) {
            b[0] = (byte) (c & 0xff);
            b[1] = (byte) ((c >> 8) & 0xff);
        } else {
            b[0] = (byte) ((c >> 8) & 0xff);
            b[1] = (byte) (c & 0xff);
        }
        return b;
    }

    public static byte[] char2bytes(char c) {
        return char2bytes(c, false);
    }

    public static char byte2char(byte highByte, byte lowByte) {
        //移位运算符优先级高于逻辑运算符
        return (char) (0xff00 & highByte << 8 | (0xff & lowByte));
    }

    /**
     *
     * @param bytes 字节数组
     * @param reverse true:小端模式，false:大端模式
     * @param start char的开始位置
     * @return
     */
    public static char bytes2char(byte[] bytes, boolean reverse, int start) {
        if (bytes.length - start < 2)
            throw new NumberFormatException("error:bytes.length - start < 2!");
        if (reverse)
            return (char) (0xff00 & bytes[start + 1] << 8 | 0xff & bytes[start]);
        else
            return (char) (0xff00 & bytes[start] << 8 | 0xff & bytes[start + 1]);
    }
    public static char bytes2char(byte[] bytes) throws NumberFormatException {
        return bytes2char(bytes, false, 0);
    }
}